package util

import (
	"errors"
	"fmt"
	"io"
	"os"
	"path"
	"path/filepath"
)

func Exists(path string) bool {
	_, err := os.Stat(path)
	if errors.Is(err, os.ErrNotExist) {
		return false
	}
	return true
}

func IsFile(path string) bool {
	fi, err := os.Stat(path)
	if errors.Is(err, os.ErrNotExist) {
		return false
	}
	return !fi.IsDir()
}

// FindInParentFolder find file in parent folder
func FindInParentFolder(cwd, configName string) (filename string, err error) {
	if cwd == "" {
		if cwd, err = os.Getwd(); err != nil {
			return "", err
		}
	}
	filename = path.Join(cwd, configName)
	if Exists(filename) && IsFile(filename) {
		return
	}
	if cwd != path.Dir(cwd) {
		if filename, err = FindInParentFolder(path.Dir(cwd), configName); err == nil {
			return
		}
	}
	err = fmt.Errorf("not found %s", configName)
	return
}

func ListTerraformFile(path string) []string {
	f, err := os.Open(path)
	if err != nil {
		return nil
	}
	entries, err := f.ReadDir(-1)
	if err != nil {
		return nil
	}
	names := make([]string, 0)
	for _, entry := range entries {
		if entry.IsDir() {
			continue
		}
		if filepath.Ext(entry.Name()) == ".tf" {
			names = append(names, entry.Name())
		}
	}
	return names
}

func Walk(root string, fn func(filename string) bool) ([]string, error) {
	filenames := make([]string, 0)
	err := filepath.Walk(root, func(filename string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}
		if fn(filename) {
			if abs, e := filepath.Abs(filename); e != nil {
				return e
			} else {
				filenames = append(filenames, abs)
			}
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	return filenames, nil
}

func IsHcl(filename string) bool {
	return filepath.Ext(filename) == ".hcl"
}

func CopyDir(srcPath string, destPath string) error {
	if IsFile(srcPath) {
		return errors.New("srcPath is not a directory or not exists！")
	}

	if Exists(destPath) && IsFile(destPath) {
		return errors.New("destInfo is not a directory or not exists！")
	}

	if err := os.MkdirAll(destPath, 0777); err != nil {
		return err
	}
	if files, err := Walk(srcPath, func(filename string) bool { return true }); err != nil {
		return err
	} else {
		for _, file := range files {
			srcRelPath, _ := filepath.Rel(srcPath, file)
			destName := filepath.Join(destPath, srcRelPath)
			if _, err = copyFile(file, destName); err != nil {
				return err
			}
		}
	}
	return nil
}

func copyFile(src, dest string) (w int64, err error) {
	srcFile, err := os.Open(src)
	if err != nil {
		return
	}
	defer func() { _ = srcFile.Close() }()

	if err = os.MkdirAll(filepath.Dir(dest), 0777); err != nil {
		return
	}

	dstFile, err := os.Create(dest)
	if err != nil {
		return
	}
	defer func() { _ = dstFile.Close() }()

	return io.Copy(dstFile, srcFile)
}
